//
//  HPBBPOSSwiper.m
//  HPZZTong
//
//  Created by WenJun on 14-6-16.
//  Copyright (c) 2014年 Handpay. All rights reserved.
//

#import "HPBBPOSSwiper.h"
#import "EmvSwipeController.h"
#import "SwipeInfo.h"
#import "POSProcess.h"
#import "SwiperUtils.h"


#define EncryptKey @"enc_key"
#define MAX_MAC_BLOCK_LEN  240

static HPBBPOSSwiper * bbposSwiper = nil;

@interface HPBBPOSSwiper()<EmvSwipeControllerDelegate>
{
    EmvSwipeController * _cswiper;
    POSProcess * _emvInfo;
    NSString *_cardType;
    NSUInteger exchangeApduAfterOnRequestOnlineProcess_apduIndex;
}
@property (strong,nonatomic) NSString * factor;

@end

@implementation HPBBPOSSwiper

+ (id)sharedInstance
{
    if (!bbposSwiper)
        bbposSwiper = [[super allocWithZone:NULL] init];
    return bbposSwiper;
}

- (id)init
{
    self = [super initWithSwiperQueue:dispatch_get_main_queue() wrapperQueue:dispatch_queue_create("wrapperSwiper", 0)];
    return self;
}

- (void)releaseInstance
{
    [_cswiper stopAudio];
    [_cswiper releaseEmvSwipeController];
    [_cswiper setDelegate:nil];
}

- (void)activeInstance
{
    _cswiper = [EmvSwipeController sharedController];
    _cswiper.delegate = self;
    _cswiper.detectDeviceChange = YES;
    [_cswiper startAudio];
}

- (NSInvocation *)operationWithSelector:(SEL)selector
{
    NSInvocation * invocation = [NSInvocation invocationWithMethodSignature:[_cswiper methodSignatureForSelector:selector]];
    [invocation setTarget:_cswiper];
    [invocation setSelector:selector];
    return invocation;
}

- (void)getKSN:(SwiperComplete)complete
{
//    LOG(@"getKSN");
    if (self.ksn.length > 0)
    {
        ExecuteSwiperBlock(complete, [HPSwiperResult successResult:self.ksn]);
    }
    else if ([_cswiper isDevicePresent] || self.connectState >= SwiperStateDectedDevice)
    {
        [self connectStateChanged:SwiperStateConnecting];
        NSInvocation * operation = [self operationWithSelector:@selector(getDeviceInfo)];
        [self beginSwiperOperation:operation timeout:10 complete:complete];
    }
    else
    {
        ExecuteSwiperBlock(complete, [HPSwiperResult failResultWithCode:HPSwiperResultNotConnected]);
    }
}

- (void)emv:(POSProcess *)emvInfo complete:(SwiperComplete)complete
{
    _emvInfo = emvInfo;
    NSMutableDictionary *optionDict = [NSMutableDictionary dictionary];
    [optionDict setObject:[_emvInfo.time substringFromIndex:2] forKey:@"terminalTime"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"checkCardTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"setAmountTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"selectApplicationTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"onlineProcessTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"finalConfirmTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:EmvOption_StartWithForceOnline] forKey:@"emvOption"];
    [optionDict setObject:[NSString stringWithFormat:@"%@80",_emvInfo.time] forKey:@"randomNumber"];
    NSInvocation * operation = [self operationWithSelector:@selector(startEmvWithData:)];
    [operation setArgument:&optionDict atIndex:2];
    [self beginSwiperOperation:operation timeout:150 complete:complete];
}

- (void)encryptKeyData:(NSString *)keyData factor:(NSString *)factor complete:(SwiperComplete)complete
{
    [self encyptData:keyData factor:factor type:@"13" complete:complete];
}

- (void)encryptPin:(NSString *)pinStr factor:(NSString *)factor complete:(SwiperComplete)complete
{
    [self encyptData:pinStr factor:factor type:@"14" complete:complete];
}

- (void)encryptMac:(NSString *)mac factor:(NSString *)factor complete:(SwiperComplete)complete
{
    __block BOOL isNeedMulStep = NO;
    int index = 0;
    unsigned char tempApdu[1024] = {0};
    char tempStr[512] = {0};
    const char* strByte = [mac UTF8String];
    int len = (int)[mac length] + 1;// 必须补80
    int needLen = len % 8;
    if (needLen > 0) {
        len += (8 - needLen);
    }
    len += 8; // 最前面强制补8个
    tempApdu[index++] = 0x80;
    tempApdu[index++] = 0xFA;
    int realLen = len;
    isNeedMulStep = (len > MAX_MAC_BLOCK_LEN);
    if (isNeedMulStep) {
        tempApdu[index++] = 0x07;
        realLen = MAX_MAC_BLOCK_LEN;
    }else{
        tempApdu[index++] = 0x05;
    }
    tempApdu[index++] = 0x00;
    tempApdu[index++] = realLen;
    index += 8;
    memcpy(tempApdu + index, strByte, [mac length]);
    index += [mac length];
    tempApdu[index] = 0x80;
    [SwiperUtils hexDecode:tempApdu lenght:realLen+5 outputStrPoint:tempStr];
    
    NSString* macApdu = [NSString stringWithUTF8String:tempStr];
    NSData * tempData = [NSData dataWithBytes:tempApdu length:1024];
    
    __block SwiperComplete callback = complete;
    
    [self encyptData:macApdu factor:factor type:@"15" complete:^(HPSwiperResult *swiperResult) {
        if (!isNeedMulStep)
        {
            callback(swiperResult);
            return;
        }
        __block BOOL isNeedMulStep3 = ((len - realLen) > MAX_MAC_BLOCK_LEN);
        
        char tempStr2[512] = {0};
        unsigned char tempApdu2[1024] = {0};
        Byte *bytes = (Byte *)[tempData bytes];
        memcpy(tempApdu2, bytes, 1024);
        
        if (isNeedMulStep3)
        {
            tempApdu2[2] = 0x03;
            tempApdu2[4] = realLen;
            [SwiperUtils hexDecode:tempApdu2 lenght:5 outputStrPoint:tempStr2];
            [SwiperUtils hexDecode:tempApdu2 + realLen + 5 lenght:realLen outputStrPoint:tempStr2 + 10];
        }
        else
        {
            tempApdu2[2] = 0x01;
            tempApdu2[4] = len - realLen;
            [SwiperUtils hexDecode:tempApdu2 lenght:5 outputStrPoint:tempStr2];
            [SwiperUtils hexDecode:tempApdu2 + realLen + 5 lenght:len - realLen outputStrPoint:tempStr2+10];
        }
        NSString *macApdu2 = [NSString stringWithUTF8String:tempStr2];
        NSData * tempData2 = [NSData dataWithBytes:tempApdu2 length:1024];
        
        [self encyptData:macApdu2 factor:factor type:@"15" complete:^(HPSwiperResult *swiperResult) {
            
            if (!isNeedMulStep3)
            {
                callback(swiperResult);
                return;
            }
            char tempStr3[512] = {0};
            unsigned char tempApdu3[1024] = {0};
            Byte *bytes2 = (Byte *)[tempData2 bytes];
            memcpy(tempApdu3, bytes2, 1024);
            tempApdu3[2] = 0x01;
            tempApdu3[4] = len - realLen*2;
            [SwiperUtils hexDecode:tempApdu3 lenght:5 outputStrPoint:tempStr3];
            [SwiperUtils hexDecode:tempApdu3 + realLen*2 + 5 lenght:len - realLen*2 outputStrPoint:tempStr3 + 10];
            
            NSString *macApdu3 = [NSString stringWithUTF8String:tempStr3];
            [self encyptData:macApdu3 factor:factor type:@"15" complete:^(HPSwiperResult *swiperResult) {
                callback(swiperResult);
            }];
        }];
    }];
}

- (void)onReturnReversalData:(NSString *)tlv {}
- (void)onReturnKsn:(NSDictionary *)ksnDict {}

// Callback of Request
- (void)onRequestTerminalTime
{
    NSString *terminalTime = @""; //e.g. 130108152010 (YYMMddHHmmss)
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"YYMMddHHmmss"];
    [formatter setTimeZone:[NSTimeZone localTimeZone]];
    terminalTime = [formatter stringFromDate:[NSDate date]];

    [[EmvSwipeController sharedController] sendTerminalTime:terminalTime];
}

- (void)onRequestDisplayText:(DisplayText)displayMessage
{
    switch (displayMessage)
    {
        case DisplayText_INSERT_CARD:
            [self operationFinishedWithResult:[HPSwiperResult failResultWithCode:HPSwiperResultRemoveICCard]];
            break;
        default:
            break;
    }
    
}
- (void)onReturnViposExchangeApduResult:(NSString *)apdu
{
    if (exchangeApduAfterOnRequestOnlineProcess_apduIndex > 0)
    {
        if (exchangeApduAfterOnRequestOnlineProcess_apduIndex == 1)
        {
            exchangeApduAfterOnRequestOnlineProcess_apduIndex = 2;
            [_cswiper viposExchangeApdu:@"80fa000018363232383738303231323231393330373030000000000000"];
        }
        else if (exchangeApduAfterOnRequestOnlineProcess_apduIndex == 2)
        {
            exchangeApduAfterOnRequestOnlineProcess_apduIndex = -1;
            [_cswiper sendOnlineProcessResult:@"8A023030"];
        }
    }
    else
    {
        NSString * value = nil;
        if ([self checkResponse:apdu value:&value])
            [self operationFinishedWithResult:[HPSwiperResult successResult:value]];
        else
            [self operationFinishedWithResult:[HPSwiperResult failResultWithMsg:nil]];
    }
}

- (void)onReturnEmvCardNumber:(NSString *)cardNumber
{
    if (!cardNumber.length)
    {
        [self operationFinishedWithResult:[HPSwiperResult failResultWithCode:HPSwiperResultRemoveICCard]];
        return;
    }
    
    SwipeInfo * swipeInfo = [[SwipeInfo alloc] init];
    swipeInfo.plainAccount = cardNumber;
    swipeInfo.icMode = YES;
    [self operationFinishedWithResult:[HPSwiperResult successResult:swipeInfo]];
}

- (void)encyptData:(NSString *)data factor:(NSString *)factor type:(NSString *)type complete:(SwiperComplete)complete
{
    if ([type isEqualToString:@"13"])
        data = [self packageDesApdu:data];
    else if ([type isEqualToString:@"14"])
        data = [NSString stringWithFormat:@"80FA00000806%@FFFFFFFF", data];
    
    NSMutableDictionary * apduDic = [NSMutableDictionary dictionary];
    [apduDic setObject:@[type, [factor substringToIndex:factor.length - 2], data] forKey:EncryptKey];
    NSInvocation * operation = [self operationWithSelector:@selector(viposBatchExchangeApdu:)];
    
    [operation setArgument:&apduDic atIndex:2];
    [self beginSwiperOperation:operation timeout:10 complete:complete];
}

- (void)onBatteryLow:(BatteryStatus)batteryStatus
{
    if (batteryStatus == BatteryStatus_Low || batteryStatus == BatteryStatus_CriticallyLow)
    {
        [_cswiper stopAudio];
        [self operationFinishedWithResult:[HPSwiperResult failResultWithCode:HPSwiperResultLowBattery]];
    }
}

//收款功能加密金额特殊处理，快速转账金额直接用关键数据加密
- (void)encyptAmount:(NSString *)amount factor:(NSString *)factor complete:(SwiperComplete)complete
{
    amount = [self packageAmount:amount];
    NSMutableDictionary * apduDic = [NSMutableDictionary dictionary];
    [apduDic setObject:@[@"13", [factor substringToIndex:factor.length - 2], amount] forKey:EncryptKey];
    NSInvocation * operation = [self operationWithSelector:@selector(viposBatchExchangeApdu:)];
    [operation setArgument:&apduDic atIndex:2];
    [self beginSwiperOperation:operation timeout:10 complete:complete];
}

- (NSString *)packageAmount:(NSString *)amount
{
    NSMutableString * result = [NSMutableString stringWithString:amount];
    
    for (NSInteger index = 0; result.length < 16; index ++)
        [result appendString:@"0"];
    
    return [NSString stringWithFormat:@"80FA000010%@",result];
}

// 确认金额
- (void)affirmAmount:(NSString *)amount time:(int)time complete:(SwiperComplete)complete
{
    complete([HPSwiperResult successResult:nil]);
}

// 刷卡
- (void)swipe:(int)time factor:(NSString *)fact complete:(SwiperComplete)complete
{
    _factor = fact;
    
    NSMutableDictionary *optionDict = [NSMutableDictionary dictionary];
    [optionDict setObject:[NSNumber numberWithInt:time] forKey:@"checkCardTimeout"];
    
    [optionDict setObject:_factor forKey:@"randomNumber"];
    
    NSInvocation * operation = [self operationWithSelector:@selector(checkCard:)];
    [operation setArgument:&optionDict atIndex:2];
    [self beginSwiperOperation:operation timeout:1000 complete:complete];
}

- (void)onReturnCancelCheckCardResult:(BOOL)isSuccess{}

- (void)onReturnTransactionResult:(TransactionResult)result data:(NSDictionary *)data
{
    if (result == TransactionResult_Approved)
        [self operationFinishedWithResult:[HPSwiperResult successResult:_emvInfo]];
    else
    {
        [self operationFinishedWithResult:[HPSwiperResult failResultWithCode:HPSwiperResultEMVFailed]];
        [_cswiper cancelCheckCard];
    }
    _emvInfo = nil;
}

- (void)onReturnCheckCardResult:(CheckCardResult)result cardDataDict:(NSDictionary *)cardDataDict
{
    switch (result)
    {
        case CheckCardResult_SwipedCard:
        case CheckCardResult_OnlyTrack2:
        case CheckCardResult_NFC_Track2:
        {
            HPSwiperResult * result = nil;
            NSString* maskPan = [cardDataDict objectForKey:@"maskedPAN"];
            
            if (maskPan.length)
            {
                if ([self checkIsICCard:cardDataDict])
                {
                    result = [HPSwiperResult failResultWithCode:HPSwiperResultDownGrage];
                    [self operationFinishedWithResult:result];
                    return;
                }
                result = [HPSwiperResult successResult:[self swipeInfoFromData:cardDataDict]];
                [self operationFinishedWithResult:result];
            }
            else
            {
                result = [HPSwiperResult failResultWithMsg:nil];
                [self operationFinishedWithResult:result];
            }
            break;
        }
        case CheckCardResult_NoCard:
        {
            break;
        }
        case CheckCardResult_BadSwipe:
        case CheckCardResult_NotIccCard:
            [self stopSwiper];
            [self operationFinishedWithResult:[HPSwiperResult failResultWithMsg:@""]];
            break;
        case CheckCardResult_InsertedCard:
            _cardType = nil;
            [_cswiper getEmvCardNumber];
            break;
        case CheckCardResult_NoResponse:
            break;
        case CheckCardResult_MagHeadFail:
            break;
        case CheckCardResult_UseIccCard:
            break;
        default:
            break;
    }
}

- (void)onRequestPinEntry{ [_cswiper sendPinEntryResult:@"123456"]; }

- (void)onRequestSetAmount
{
    [_cswiper setAmount:@"1.00" cashbackAmount:@"" currencyCode:@"156" transactionType:TransactionType_Goods];
}

- (void)onRequestFinalConfirm { [_cswiper sendFinalConfirmResult:YES]; }

- (void)onRequestSelectApplication:(NSArray *)applicationArray
{
    if (_cardType)
    {
        for (int i = 0; i < applicationArray.count; i++)
        {
            NSString* str = [applicationArray objectAtIndex:i];
            if ([str isEqualToString:_cardType])
            {
                [_cswiper selectApplication:i];
                return;
            }
        }
    }
    else
    {
        HPSwiperResult* swiperResult = [[HPSwiperResult alloc]initWithCode:HPSwiperResultSelectApplication message:nil value:applicationArray];
        [self operationFinishedWithResult:swiperResult];
    }
}

- (void)onRequestOnlineProcess:(NSString *)tlv
{
    NSDictionary *tlvDict = [_cswiper decodeTlv:tlv];
    NSString* maskPan = [tlvDict objectForKey:@"maskedPAN"];

    if (maskPan.length)
    {
        [self swipeInfoFromDataICC:tlv];
        [self operationFinishedWithResult:[HPSwiperResult successResult:nil]];
        exchangeApduAfterOnRequestOnlineProcess_apduIndex = 1;
        [_cswiper viposExchangeApdu:@"801a1301082012101111245180"];
    }
    else
        [self operationFinishedWithResult:[HPSwiperResult failResultWithMsg:nil]];
}

- (void)startEmvWithData
{
    NSString *terminalTime = @""; //e.g. 130108152010 (YYMMddHHmmss)
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"YYMMddHHmmss"];
    [formatter setTimeZone:[NSTimeZone localTimeZone]];
    terminalTime = [formatter stringFromDate:[NSDate date]];
    
    NSMutableDictionary *optionDict = [NSMutableDictionary dictionary];
    [optionDict setObject:terminalTime forKey:@"terminalTime"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"checkCardTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"setAmountTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"selectApplicationTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"onlineProcessTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:30] forKey:@"finalConfirmTimeout"];
    [optionDict setObject:[NSNumber numberWithInt:EmvOption_StartWithForceOnline] forKey:@"emvOption"];
    
    [optionDict setObject:self.factor forKey:@"randomNumber"];
    [_cswiper startEmvWithData:optionDict];
}

//// 密码输入
//- (void)inputPwd:(int)time factor:(NSString *)factor complete:(SwiperComplete)complete
//{
//
//}

- (void)onInterrupted{}

- (SwipeInfo *)swipeInfoFromData:(NSDictionary *)decodeData
{
    SwipeInfo * swipeInfo = [[SwipeInfo alloc] init];
    NSMutableString * track2Str = [NSMutableString stringWithString:[decodeData objectForKey:@"encTrack2"]];
    
    while (track2Str.length < 40)
        [track2Str appendString:@"F"];
    
    swipeInfo.encTrack2 = track2Str;
    swipeInfo.plainAccount = [decodeData objectForKey:@"maskedPAN"];
    swipeInfo.dynamicKey = [[decodeData objectForKey:@"randomNumber"] uppercaseString];
    swipeInfo.icMode = NO;
    return swipeInfo;
}

- (void)swipeInfoFromDataICC:(NSString *)strData
{
    NSDictionary *decodeData = [_cswiper decodeTlv:strData];
    if ([decodeData objectForKey:@"70"] != nil)
        decodeData = [_cswiper decodeTlv:[decodeData objectForKey:@"70"]];
    
    SwipeInfo * swipeInfo = _emvInfo.swipeInfo;
    NSMutableString * track2Str = [NSMutableString stringWithString:[decodeData objectForKey:@"encTrack2Eq"]];
    
    while (track2Str.length < 40)
        [track2Str appendString:@"F"];
    
    swipeInfo.encTrack2 = track2Str;
    swipeInfo.plainAccount = [decodeData objectForKey:@"maskedPAN"];
    swipeInfo.encAccount = [decodeData objectForKey:@"encPAN"];
    swipeInfo.dynamicKey = [[decodeData objectForKey:@"encTrack2EqRandomNumber"] uppercaseString];
    swipeInfo.icNumber = [decodeData objectForKey:@"5F34"];
    
    if (swipeInfo.icNumber.length == 2)
        swipeInfo.icNumber = [NSString stringWithFormat:@"0%@", swipeInfo.icNumber];
    
    swipeInfo.dcData = [_cswiper viposGetIccData:strData];
    swipeInfo.icMode = YES;
}

- (void)onError:(ErrorType)ErrorType errorMessage:(NSString *)errorMessage
{
    [self operationFinishedWithResult:[HPSwiperResult failResultWithCode:HPSwiperResultFail]];
}

-(void)selectApp:(NSArray*)type time:(int)time factor:(NSString *)factor complete:(SwiperComplete)complete
{
    NSNumber* number = [type objectAtIndex:2];
    _cardType = [type objectAtIndex:1];
    [_cswiper selectApplication:number.intValue];
}

- (void)onWaitingForCard {}
- (void)onNoDeviceDetected {}

- (void)onReturnDeviceInfo:(NSDictionary *)deviceInfoDict
{
    NSString *csn = [deviceInfoDict objectForKey:@"csn"];
    if (!csn.length)
        [self operationFinishedWithResult:[HPSwiperResult failResultWithMsg:nil]];
    else{
        if (self.connectState == SwiperStateConnecting)
        {
            self.ksn = csn;
            [self connectStateChanged:SwiperStateConnected];
        }
        [self operationFinishedWithResult:[HPSwiperResult successResult:self.ksn]];
    }
}

- (void)onReturnViposBatchExchangeApduResult:(NSDictionary *)data
{
    if ([[data allKeys] containsObject:EncryptKey])
    {
        NSString * value = nil;
        if ([self checkResponse:[data objectForKey:EncryptKey] value:&value])
            [self operationFinishedWithResult:[HPSwiperResult successResult:value]];
        else
            [self operationFinishedWithResult:[HPSwiperResult failResultWithMsg:nil]];
    }
    else
        [self operationFinishedWithResult:[HPSwiperResult successResult:data]];
}


-(NSString*)packageDesApdu:(NSString*)str
{
    int index = 0;
    unsigned char tempApdu[512] = {0};
    char tempStr[512] = {0};
    const char* strByte = [str UTF8String];
    int len = (int)[str length];
    int needLen = len % 8;
    if (needLen > 0)
        len += (8 - needLen);
    
    tempApdu[index++] = 0x80;
    tempApdu[index++] = 0xFA;
    tempApdu[index++] = 0x00;
    tempApdu[index++] = 0x00;
    tempApdu[index++] = len;
    
    memcpy(tempApdu + index, strByte, [str length]);
    [SwiperUtils hexDecode:tempApdu lenght:len+index outputStrPoint:tempStr];
    return [NSString stringWithUTF8String:tempStr];
}

- (void)onDevicePlugged { [self connectStateChanged:SwiperStateDectedDevice]; }
- (void)onDeviceUnplugged { [self connectStateChanged:SwiperStateNoDeviceDected]; }

- (BOOL)checkResponse:(NSString *)response value:(NSString **)value
{
    if ([response hasSuffix:@"9000"])
    {
        *value = [response substringToIndex:[response length] - 4];
        return YES;
    }
    return NO;
}

- (void)stopSwiper
{
    [super stopSwiper];
    [_cswiper cancelCheckCard];
}

- (BOOL)canInputPin { return NO; }
@end


